package com.ticket.system.service.impl;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.ticket.common.constant.PerformConstants;
import com.ticket.common.core.domain.model.LoginUser;
import com.ticket.common.exception.ServiceException;
import com.ticket.common.utils.DateUtils;
import com.ticket.common.utils.SecurityUtils;
import com.ticket.common.utils.StringUtils;
import com.ticket.system.domain.*;
import com.ticket.system.domain.enu.*;
import com.ticket.system.domain.vo.*;
import com.ticket.system.mapper.*;
import com.ticket.system.service.*;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;

import static java.util.stream.Collectors.toList;

/**
 * 退款单Service业务层处理
 * 
 * @author ruoyi
 * @date 2023-10-31
 */
@Service
@AllArgsConstructor
public class YanchuRefundOrderServiceImpl implements IYanchuRefundOrderService 
{
    private static final Logger logger = LoggerFactory.getLogger(YanchuRefundOrderServiceImpl.class);
    @Autowired
    private YanchuRefundOrderMapper yanchuRefundOrderMapper;
    @Autowired
    private YanchuOrderMapper yanchuOrderMapper;

    @Autowired
    private YanchuOrderDetailMapper yanchuOrderDetailMapper;
    @Autowired
    private YanchuSeatMapper yanchuSeatMapper;
    @Autowired
    private WxPayService wxPayService;

    @Value("${notifyUrl}")
    private String notifyUrl;
    @Value("${tempUrl}")
    private String tempUrl;
    @Autowired
    private IYanchuScoreService iYanchuScoreService;
    @Autowired
    private IYanchuSeatService iYanchuSeatService;

    @Autowired
    private YuanchuRefundOrderDetailMapper yuanchuRefundOrderDetailMapper;

    private WxMaService wxMaService;
    @Value("${wx.miniapp.templateIdCancel}")
    private String templateIdCancel;
    @Value("${wx.miniapp.miniprogramState}")
    private String miniprogramState;

    @Autowired
    private IYanchuMovieCardService yanchuMovieCardService;
    @Autowired
    private IYanchuStorageCradService yanchuStorageCradService;

    @Autowired
    private YanchuConsumeRecordMapper yanchuConsumeRecordMapper;
    @Autowired
    private YanchuSessionMapper yanchuSessionMapper;
    @Value("${TOP_OPERA_ID}")
    private Long topOperaId;

    /**
     * 查询退款单
     * 
     * @param id 退款单主键
     * @return 退款单
     */
    @Override
    public YanchuRefundOrder selectYanchuRefundOrderById(Long id)
    {
        return yanchuRefundOrderMapper.selectYanchuRefundOrderById(id);
    }

    @Override
    public RefundOrderVo selectRefundOrderVoById(Long id)
    {
        RefundOrderVo vo = yanchuRefundOrderMapper.selectRefundOrderVoById(id);
        if(null != vo){
            List<OrderDetailVo> list = yanchuOrderDetailMapper.selectRefundDetailVoList(id);
            vo.setDetailVoList(list);
        }
        return vo;
    }
    @Override
    public RefundOrderVo selectRefundOrderVoByDetailId(Long id)
    {
        RefundOrderVo vo = yanchuRefundOrderMapper.selectRefundOrderVoByDetailId(id);
//        if(null != vo){
//            List<OrderDetailVo> list = yanchuOrderDetailMapper.selectRefundDetailVoList(id);
//            vo.setDetailVoList(list);
//        }
        return vo;
    }

    /**
     * 查询退款单列表
     * 
     * @param yanchuRefundOrder 退款单
     * @return 退款单
     */
    @Override
    public List<RefundOrderVo> selectYanchuRefundOrderList(YanchuRefundOrder yanchuRefundOrder)
    {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        logger.info("selectYanchuRefundOrderList loginUser.getUser().getOperaId() : {}",loginUser.getUser().getOperaId());
        if(null != loginUser.getUser().getOperaId() && !loginUser.getUser().getOperaId().equals(topOperaId)){
            yanchuRefundOrder.setOperaId(loginUser.getUser().getOperaId());
        }
        return yanchuRefundOrderMapper.selectYanchuRefundOrderList(yanchuRefundOrder);
    }

    /**
     * 新增退款单
     * 
     * @param refundOrder 退款单
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertYanchuRefundOrder(YanchuRefundOrder refundOrder,YanchuSession session) throws WxPayException{
        logger.info("insertYanchuRefundOrder orderId:{} refundMoney:{} detailIds:{}",refundOrder.getOrderId(),
                refundOrder.getRefundTotalMoney(),refundOrder.getDetailIds());
        YanchuOrder yanchuOrder = yanchuOrderMapper.selectOrderById(refundOrder.getOrderId());
        if(null == yanchuOrder || (!yanchuOrder.getStatus().equals(PerformConstants.ORDER_PAY) &&
                !yanchuOrder.getStatus().equals(PerformConstants.ORDER_CHECKED))){
            throw new ServiceException("订单不存在或状态不正确!");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String refundNo = DateUtils.dateTimeNow() + yanchuOrder.getUserId() + RandomUtil.randomInt(1, 1000);
        refundOrder.setCreateTime(DateUtils.getNowDate());
        refundOrder.setRefundNo(refundNo);
        refundOrder.setRefundStatus(PerformConstants.REFUND_INIT);
        refundOrder.setOrderNo(yanchuOrder.getOrderId());
        refundOrder.setCreateTime(new Date());
        refundOrder.setCreator(loginUser.getUser().getUserId());
        String ids = refundOrder.getDetailIds();
        List<Long> idList = Splitter.on(",").trimResults().omitEmptyStrings().splitToList(ids).stream().map(Long::valueOf).collect(toList());

        int count = 0;
        List<YuanchuRefundOrderDetail> details = Lists.newArrayList();
        YuanchuRefundOrderDetail detail = null;
        //1：整单退款；2：部分退款
        if(refundOrder.getRefundTotalMoney().compareTo(yanchuOrder.getTotalMoney())==0){
            refundOrder.setRefundFlag(PerformConstants.REFUND_FLAG_TOTAL);
            refundOrder.setCardAmt(yanchuOrder.getCardAmt());
            refundOrder.setBalanceAmt(yanchuOrder.getBalanceAmt());
            refundOrder.setWechatAmt(yanchuOrder.getWechatAmt());
            count = yanchuRefundOrderMapper.insertYanchuRefundOrder(refundOrder);
            for(Long id:idList){
                detail = new YuanchuRefundOrderDetail();
                detail.setRefundOrderId(refundOrder.getId());
                detail.setOrderDetailId(id);
                detail.setOrderId(refundOrder.getOrderId());
                details.add(detail);
            }
            yuanchuRefundOrderDetailMapper.insertBatchRefundOrderDetail(details);
            cancelOrder(yanchuOrder,refundOrder,session,idList);//取消整个订单
        } else {
            refundOrder.setRefundFlag(PerformConstants.REFUND_FLAG_PART);
            count = yanchuRefundOrderMapper.insertYanchuRefundOrder(refundOrder);
            List<Long> detailsVo = yanchuOrderDetailMapper.selectAreIds(idList);
            if(CollectionUtils.isNotEmpty(detailsVo)){
                List<YanchuOrderDetail> orderDetails = yanchuOrderDetailMapper.selectOrderDetailsByAreaId(detailsVo,yanchuOrder.getId());//查询所有已支付组合票
                for(Long id:idList){
                    detail = new YuanchuRefundOrderDetail();
                    detail.setRefundOrderId(refundOrder.getId());
                    detail.setOrderDetailId(id);
                    detail.setOrderId(refundOrder.getOrderId());
                    details.add(detail);
                }
                for(YanchuOrderDetail detail1:orderDetails){
                    if(idList.contains(detail1.getId())){
                        continue;
                    }
                    idList.add(detail1.getId());
                    detail = new YuanchuRefundOrderDetail();
                    detail.setRefundOrderId(refundOrder.getId());
                    detail.setOrderDetailId(detail1.getId());
                    detail.setOrderId(refundOrder.getOrderId());
                    refundOrder.setRefundTotalMoney(refundOrder.getRefundTotalMoney().add(detail1.getMoney()));
                    details.add(detail);
                }
                if(orderDetails.size() == details.size()){
                    refundOrder.setRefundFlag(PerformConstants.REFUND_FLAG_TOTAL);
                }
                yanchuRefundOrderMapper.updateYanchuRefundOrder(refundOrder);
            }else{
                List<YanchuOrderDetail> payDetails = yanchuOrderDetailMapper.selectPayedOrderDetailList(yanchuOrder.getId());
                if(CollectionUtils.isNotEmpty(payDetails) && payDetails.size() == idList.size()){
                    refundOrder.setRefundFlag(PerformConstants.REFUND_FLAG_TOTAL);
                    BigDecimal wechatLeft = yanchuOrder.getWechatAmt().subtract(yanchuOrder.getWechatRefundAmt());
                    if(wechatLeft.compareTo(BigDecimal.ZERO)>0){
                        refundOrder.setWechatAmt(wechatLeft);
                    }
                    BigDecimal balanceLeft = yanchuOrder.getBalanceAmt().subtract(yanchuOrder.getBalanceRefundAmt());
                    if(balanceLeft.compareTo(BigDecimal.ZERO)>0){
                        refundOrder.setBalanceAmt(balanceLeft);
                    }
                    BigDecimal cardLeft = yanchuOrder.getCardAmt().subtract(yanchuOrder.getCardRefundAmt());
                    if(cardLeft.compareTo(BigDecimal.ZERO)>0){
                        refundOrder.setCardAmt(cardLeft);
                    }
                }
                for(Long id:idList){
                    detail = new YuanchuRefundOrderDetail();
                    detail.setRefundOrderId(refundOrder.getId());
                    detail.setOrderDetailId(id);
                    detail.setOrderId(refundOrder.getOrderId());
                    details.add(detail);
                }
            }
            yuanchuRefundOrderDetailMapper.insertBatchRefundOrderDetail(details);
            if(PerformConstants.REFUND_FLAG_TOTAL.equals(refundOrder.getRefundFlag())){
                int countCancel = cancelOrder(yanchuOrder,refundOrder,session,idList);
                logger.info("整单退countCancel:{}",countCancel);
            }else{
                int countCancel = cancelTicketOrder(refundOrder,yanchuOrder,idList,session);
                logger.info("部分退countCancel:{}",countCancel);
            }
        }
        return count;
    }

    /**
     * 修改退款单
     * 
     * @param yanchuRefundOrder 退款单
     * @return 结果
     */
    @Override
    public int updateYanchuRefundOrder(YanchuRefundOrder yanchuRefundOrder)
    {
        return yanchuRefundOrderMapper.updateYanchuRefundOrder(yanchuRefundOrder);
    }

    /**
     * 批量删除退款单
     * 
     * @param ids 需要删除的退款单主键
     * @return 结果
     */
    @Override
    public int deleteYanchuRefundOrderByIds(Long[] ids)
    {
        return yanchuRefundOrderMapper.deleteYanchuRefundOrderByIds(ids);
    }

    /**
     * 删除退款单信息
     * 
     * @param id 退款单主键
     * @return 结果
     */
    @Override
    public int deleteYanchuRefundOrderById(Long id)
    {
        return yanchuRefundOrderMapper.deleteYanchuRefundOrderById(id);
    }

    /**
     * 取消单个订单
     *
     * @param yanchuOrder
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int cancelOrder(YanchuOrder yanchuOrder,YanchuRefundOrder refundOrder,YanchuSession session,List<Long> idList) throws WxPayException {
        int count = 0;
        logger.info("cancelOrder RefundNo:{} orderNo:{} cardAmt:{} balanceAmt:{} wechatAmt:{}",refundOrder.getRefundNo(),
                yanchuOrder.getOrderId(),refundOrder.getCardAmt(),refundOrder.getBalanceAmt(),refundOrder.getWechatAmt());
        //退观演卡
        if(null != refundOrder.getCardAmt() && refundOrder.getCardAmt().compareTo(BigDecimal.ZERO)>0){
            YanchuConsumeRecord consumeRecord = new YanchuConsumeRecord();
            consumeRecord.setOrderId(yanchuOrder.getId());
            consumeRecord.setBindId(yanchuOrder.getUserId());
            consumeRecord.setCardType(PerformConstants.CARD_TYPE_CARD);
            consumeRecord.setConType(PerformConstants.CONSUME_TYPE_CARD);
            List<YanchuConsumeRecord> list = yanchuConsumeRecordMapper.selectYanchuConsumeRecordList(consumeRecord);//所有观演卡消费记录
            if(CollectionUtils.isNotEmpty(list)){
                for(YanchuConsumeRecord record:list){
                    yanchuMovieCardService.refundByRecord(record,null);
                }
            }
        }
        //退存储卡
        if(null != refundOrder.getBalanceAmt() && refundOrder.getBalanceAmt().compareTo(BigDecimal.ZERO)>0){
            yanchuStorageCradService.refund(yanchuOrder,refundOrder);
        }
		LoginUser loginUser = SecurityUtils.getLoginUser();
        //退微信余额
		if(null != refundOrder.getWechatAmt() && refundOrder.getWechatAmt().compareTo(BigDecimal.ZERO)>0){
            WxPayRefundRequest request = WxPayRefundRequest.newBuilder()
                    .outTradeNo(yanchuOrder.getOrderId())
                    .outRefundNo(refundOrder.getRefundNo())
                    .totalFee(yuanToFee(yanchuOrder.getWechatAmt()))
                    .refundFee(yuanToFee(refundOrder.getWechatAmt()))
                    .notifyUrl(notifyUrl + "/system/orderRefund/refundNotify/forAllUser")
                    .build();

            WxPayRefundResult wxPayRefundResult = wxPayService.refund(request);
            //判断退款信息是否正确
            if (PerformConstants.REFUND_SUCCESS.equals(wxPayRefundResult.getReturnCode())
                    && PerformConstants.REFUND_SUCCESS.equals(wxPayRefundResult.getResultCode())) {
                refundOrder.setRefundStatus(PerformConstants.REFUND_PROC);
                yanchuRefundOrderMapper.updateYanchuRefundOrder(refundOrder);
                if(refundOrder.getRefundFlag().equals(PerformConstants.REFUND_FLAG_TOTAL)){//整单退
                    //更新订单表
                    count = yanchuOrderMapper.updateRefundById(yanchuOrder.getId(), PerformConstants.ORDER_REFUNDING,
                            null,refundOrder.getRefundNo(),loginUser.getUser().getUserId(),
                            refundOrder.getCardAmt(),refundOrder.getBalanceAmt(),refundOrder.getWechatAmt());
                    if (count == 0) {
                        logger.warn("更新支付失败 orderNo:{}", yanchuOrder.getOrderId());
                        throw new ServiceException("更新退款状态失败！");
                    }
                }
                //更新订单详情表
                yanchuOrderDetailMapper.updateOrderDetailById(idList, PerformConstants.ORDER_REFUNDING,null);
            }else{
                throw new ServiceException("微信退款失败！");
            }
        } else {
            //直接更新为已退款
            refundOrder.setRefundStatus(PerformConstants.REFUND_DEALED);
            yanchuRefundOrderMapper.updateYanchuRefundOrder(refundOrder);
            if(refundOrder.getRefundFlag().equals(PerformConstants.REFUND_FLAG_TOTAL)){//整单退
                //更新订单表
                count = yanchuOrderMapper.updateRefundById(yanchuOrder.getId(), PerformConstants.ORDER_REFUND,
                        null,refundOrder.getRefundNo(),loginUser.getUser().getUserId(),
                        refundOrder.getCardAmt(),refundOrder.getBalanceAmt(),refundOrder.getWechatAmt());
                if (count == 0) {
                    logger.warn("更新支付失败 orderNo:{}", yanchuOrder.getOrderId());
                    throw new ServiceException("更新退款状态失败！");
                }
            }

            //更新订单详情表
            count = yanchuOrderDetailMapper.updateOrderDetailById(idList, PerformConstants.ORDER_REFUND,
                    null);
            if (count > 0 && (null==session || !session.getStatus().equals(PerformConstants.SESSION_CANCEL))) {
                //更新座位表
                int seatCount = yanchuSeatMapper.updateSeatByOrderId(yanchuOrder.getId(),
                        PerformConstants.TICKET_SEAT_OPEN, PerformConstants.TICKET_SEAT_PAY);
                if (seatCount == 0) {
                    logger.warn("更新座位失败 outTradeNo:{} seatCount：{} detailCount:{}", yanchuOrder.getOrderId(), seatCount, count);
                    throw new ServiceException("更新座位状态失败！");
                }else{
                    YanchuSeat yanchuSeat = new YanchuSeat();
                    yanchuSeat.setOrderId(yanchuOrder.getId());
                    List<YanchuSeat> seats = yanchuSeatMapper.selectYanchuSeatList(yanchuSeat);
                    iYanchuSeatService.refreshTicketList(seats,yanchuOrder.getSessionId());
                }
            }
            if(null == session){
                //更新票房、积分
                iYanchuScoreService.reduceNumsMoney(count, refundOrder.getRefundTotalMoney(), yanchuOrder.getSessionId(),
                        yanchuOrder.getUserId(), refundOrder.getRefundTotalMoney().intValue());
            }
        }

        return count;
    }

    /**
     * 退款
     *
     * @param outTradeNo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int cancelOrderNotify(String outTradeNo,String refundNo,BigDecimal refundFee) {
        logger.info("cancelOrderNotify outTradeNo:{} refundNo:{} refundFee:{}", outTradeNo,refundNo,refundFee);
        Integer count = 1;
        YanchuOrder yanchuOrder = yanchuOrderMapper.selectYanchuOrderByNo(outTradeNo);
        if (null == yanchuOrder) {
            return 1;
        }
        YanchuRefundOrder refundOrder = yanchuRefundOrderMapper.selectRefundOrderByRefundNo(refundNo);
        if (null == refundOrder) {
            return count;
        }
        Integer initStatus = PerformConstants.ORDER_REFUNDING;
        Integer toStatus = PerformConstants.ORDER_REFUND;
        if (yanchuOrder.getStatus() == PerformConstants.ORDER_REFUND) {
            logger.warn("存在重复退款回调 orderNo:{}", outTradeNo);
            return 1;
        }
        if(refundOrder.getRefundFlag().equals(PerformConstants.REFUND_FLAG_TOTAL)){//整单退款
            //更新订单表
            count = yanchuOrderMapper.updateOrderStatus(yanchuOrder.getOrderId(),null,toStatus, null);
            if (count == 0) {
                logger.warn("取消支付失败 orderNo:{} tradeNo：{}", outTradeNo);
                return 1;
            }
        }
        //更新订单详情表
        int detailCount = yanchuOrderDetailMapper.updateOrderDetailByRefundNo(yanchuOrder.getId(),refundOrder.getId(),
                toStatus, initStatus);
        refundOrder.setRefundStatus(PerformConstants.REFUND_DEALED);
        yanchuRefundOrderMapper.updateYanchuRefundOrder(refundOrder);
        YanchuSession session = yanchuSessionMapper.selectYanchuSessionById(yanchuOrder.getSessionId());
        if (detailCount > 0 && (null==session || !session.getStatus().equals(PerformConstants.SESSION_CANCEL))) {
            //更新座位表
            int seatCount = yanchuSeatMapper.updateSeatByOrderId(yanchuOrder.getId(),
                    PerformConstants.TICKET_SEAT_OPEN, PerformConstants.TICKET_SEAT_PAY);
            if (seatCount == 0) {
                logger.warn("更新座位失败 outTradeNo:{} seatCount：{} detailCount:{}", outTradeNo, seatCount, detailCount);
            }else{
                YanchuSeat yanchuSeat = new YanchuSeat();
                yanchuSeat.setOrderId(yanchuOrder.getId());
                List<YanchuSeat> seats = yanchuSeatMapper.selectYanchuSeatList(yanchuSeat);
                iYanchuSeatService.refreshTicketList(seats,yanchuOrder.getSessionId());
            }
        }
        if(null == session || !session.getStatus().equals(PerformConstants.SESSION_CANCEL)){
            //更新票房、积分
            iYanchuScoreService.reduceNumsMoney(detailCount, refundOrder.getRefundTotalMoney(), yanchuOrder.getSessionId(),
                    yanchuOrder.getUserId(), refundOrder.getRefundTotalMoney().intValue());
        }
        return count;
    }

    /**
     * 部分退票
     *
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int cancelTicketOrder(YanchuRefundOrder refundOrder,YanchuOrder yanchuOrder,List<Long> detailIds,YanchuSession session) throws WxPayException {
        int count = 0;
        BigDecimal wechatLeftAmt = yanchuOrder.getWechatAmt().subtract(yanchuOrder.getWechatRefundAmt());//微信剩余可退
        BigDecimal balanceLeftAmt = yanchuOrder.getBalanceAmt().subtract(yanchuOrder.getBalanceRefundAmt());//余额剩余可退
        BigDecimal cardLeftAmt = yanchuOrder.getCardAmt().subtract(yanchuOrder.getCardRefundAmt());//观演卡剩余可退
        BigDecimal wechatAmt = BigDecimal.ZERO;//微信退款金额
        BigDecimal balanceAmt = BigDecimal.ZERO;//储值卡退款金额
        BigDecimal cardAmt = BigDecimal.ZERO;//观演卡退款金额
        BigDecimal leftAmt = refundOrder.getRefundTotalMoney();//退款金额
        logger.info("cancelTicketOrder wechatLeftAmt:{} balanceLeftAmt:{} cardLeftAmt:{} leftAmt:{}",wechatLeftAmt,balanceLeftAmt,cardLeftAmt,leftAmt);
        //先退微信
        if (wechatLeftAmt.compareTo(BigDecimal.ZERO) > 0) {//微信可退部分
            if (leftAmt.compareTo(wechatLeftAmt) >= 0) {
                wechatAmt = wechatLeftAmt;
                leftAmt = leftAmt.subtract(wechatLeftAmt);
            } else {
                wechatAmt = leftAmt;
                leftAmt = BigDecimal.ZERO;
            }
        }
        //计算余额可退金额
        if (leftAmt.compareTo(BigDecimal.ZERO) > 0 && balanceLeftAmt.compareTo(BigDecimal.ZERO) > 0) {
            if (leftAmt.compareTo(balanceLeftAmt) >= 0) {
                balanceAmt = balanceLeftAmt;
                leftAmt = leftAmt.subtract(balanceLeftAmt);
            } else {
                balanceAmt = leftAmt;
                leftAmt = BigDecimal.ZERO;
            }
        }
        //计算观演卡可退金额
        if (leftAmt.compareTo(BigDecimal.ZERO) > 0 && cardLeftAmt.compareTo(BigDecimal.ZERO) > 0) {
            if (leftAmt.compareTo(cardLeftAmt) <= 0) {
                cardAmt = leftAmt;
            } else if (leftAmt.compareTo(cardLeftAmt) > 0) {
                throw new ServiceException("剩余退款金额不足！");
            }
        }
        logger.info("cancelTicketOrder leftAmt:{} wechatAmt:{} cardAmt:{} balanceAmt:{}",leftAmt,wechatAmt,cardAmt,balanceAmt);
        //退余额
        if (balanceAmt.compareTo(BigDecimal.ZERO) > 0) {
            refundOrder.setBalanceAmt(balanceAmt);
            yanchuStorageCradService.refund(yanchuOrder, refundOrder);
        }
        //退观演卡
        if (cardAmt.compareTo(BigDecimal.ZERO) > 0) {
                refundOrder.setCardAmt(cardAmt);
            yanchuMovieCardService.refund(refundOrder, yanchuOrder, cardAmt);
        }
        //退微信
        if (wechatAmt.compareTo(BigDecimal.ZERO) > 0) {
            refundOrder.setWechatAmt(wechatAmt);
            WxPayRefundRequest request = WxPayRefundRequest.newBuilder()
                    .outTradeNo(yanchuOrder.getOrderId())
                    .outRefundNo(refundOrder.getRefundNo())
                    .totalFee(BaseWxPayRequest.yuanToFen(yanchuOrder.getWechatAmt() + ""))
                    .refundFee(BaseWxPayRequest.yuanToFen(refundOrder.getWechatAmt() + ""))
                    .notifyUrl(notifyUrl + "/system/orderRefund/refundTicketNotify/forAllUser")
                    .build();
            WxPayRefundResult wxPayRefundResult = wxPayService.refund(request);
            //判断退款信息是否正确
            if (PerformConstants.REFUND_SUCCESS.equals(wxPayRefundResult.getReturnCode())
                    && PerformConstants.REFUND_SUCCESS.equals(wxPayRefundResult.getResultCode())) {
                refundOrder.setRefundStatus(PerformConstants.REFUND_PROC);
                yanchuRefundOrderMapper.updateYanchuRefundOrder(refundOrder);
                //更新订单表
                count = yanchuOrderDetailMapper.updateOrderDetailById(detailIds, PerformConstants.ORDER_REFUNDING,
                        null);
                if (count == 0) {
                    logger.warn("更新退款失败 orderNo:{}", detailIds);
                    throw new ServiceException("更新退款状态失败");
                }
            } else {
                throw new ServiceException("微信退款失败");
            }
            refundOrder.setRefundStatus(PerformConstants.REFUND_PROC);
            yanchuRefundOrderMapper.updateYanchuRefundOrder(refundOrder);
        } else {
            refundOrder.setRefundStatus(PerformConstants.REFUND_DEALED);
            yanchuRefundOrderMapper.updateYanchuRefundOrder(refundOrder);
            //更新订单表
            count = yanchuOrderDetailMapper.updateOrderDetailById(detailIds, PerformConstants.ORDER_REFUND,
                    null);
            if (count > 0 && (null==session || !session.getStatus().equals(PerformConstants.SESSION_CANCEL))) {
                List<YanchuOrderDetail> details = yanchuOrderDetailMapper.selectOrderDetails(detailIds);
                List<String> tickets = details.stream().map(YanchuOrderDetail::getTicket).collect(toList());
                //更新座位表
                int seatCount = yanchuSeatMapper.updateSeat(tickets,
                        PerformConstants.TICKET_SEAT_OPEN, PerformConstants.TICKET_SEAT_PAY);
                if (seatCount > 0 && (null==session || !session.getStatus().equals(PerformConstants.SESSION_CANCEL))) {
                    List<YanchuSeat> seats = yanchuSeatMapper.selectYanchuSeatByTicket(tickets);
                    iYanchuSeatService.refreshTicketList(seats,null);
                }
            }
            if(null == session || !session.getStatus().equals(PerformConstants.SESSION_CANCEL)){
                //更新票房、积分
                iYanchuScoreService.reduceNumsMoney(count, refundOrder.getRefundTotalMoney(), yanchuOrder.getSessionId(),
                        yanchuOrder.getUserId(), refundOrder.getRefundTotalMoney().intValue());
            }
        }
        yanchuOrderMapper.updateRefundAmtById(yanchuOrder.getId(), cardAmt, balanceAmt, wechatAmt);
        return count;
    }

    /**
     * 取消单张票回调
     *
     * @param outRefundNo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int cancelTicketOrderNotify(String outRefundNo, BigDecimal refundFee) {
        logger.info("部分退款cancelOrder cancelOrderTicketNotify:{} refundFee:{}", outRefundNo,refundFee);
        YanchuRefundOrder refundOrder = yanchuRefundOrderMapper.selectRefundOrderByRefundNo(outRefundNo);
        Integer count = 1;
        if (null == refundOrder) {
            return count;
        }
        Integer initStatus = PerformConstants.ORDER_REFUNDING;
        Integer toStatus = PerformConstants.ORDER_REFUND;
        if (refundOrder.getRefundStatus().equals(PerformConstants.REFUND_DEALED)) {
            logger.warn("存在重复支付回调 orderNo:{}", outRefundNo);
            return count;
        }
        YuanchuRefundOrderDetail yuanchuRefundOrderDetail = new YuanchuRefundOrderDetail();
        yuanchuRefundOrderDetail.setRefundOrderId(refundOrder.getId());
        List<YuanchuRefundOrderDetail> listDetail = yuanchuRefundOrderDetailMapper.selectYuanchuRefundOrderDetailList(yuanchuRefundOrderDetail);
        List<Long> list = listDetail.stream().map(YuanchuRefundOrderDetail::getOrderDetailId).collect(toList());

        List<YanchuOrderDetail> details = yanchuOrderDetailMapper.selectOrderDetails(list);
        refundOrder.setRefundStatus(PerformConstants.REFUND_DEALED);
        yanchuRefundOrderMapper.updateYanchuRefundOrder(refundOrder);

        List<String> tickets = details.stream().map(YanchuOrderDetail::getTicket).collect(toList());
        //更新订单详情表
        int detailCount = yanchuOrderDetailMapper.updateOrderDetailById(list,toStatus, initStatus);
        YanchuSession session = yanchuSessionMapper.selectYanchuSessionById(details.get(0).getSessionId());
        if (detailCount > 0 && (null==session || !session.getStatus().equals(PerformConstants.SESSION_CANCEL))) {
            //更新座位表
            int seatCount = yanchuSeatMapper.updateSeat(tickets,
                    PerformConstants.TICKET_SEAT_OPEN, PerformConstants.TICKET_SEAT_PAY);
            if (seatCount > 0) {
                List<YanchuSeat> seats = yanchuSeatMapper.selectYanchuSeatByTicket(tickets);
                iYanchuSeatService.refreshTicketList(seats,null);
            }
            //更新票房、积分
            iYanchuScoreService.reduceNumsMoney(detailCount, refundOrder.getRefundTotalMoney(), details.get(0).getSessionId(),
                    details.get(0).getUserId(), refundOrder.getRefundTotalMoney().intValue());
        }
        return count;
    }

    private int yuanToFee(BigDecimal bigDecimal) {
        return bigDecimal.multiply(new BigDecimal(100)).intValue();
    }

    /**
     * 取消已支付订单
     * @param yanchuOrder
     * @param reason
     * @param userId
     * @return
     */
    @Override
    @Async
    public Integer cancelOrderList(YanchuOrder yanchuOrder,String reason,Long userId,YanchuSession session){
        List<OrderVo> list = yanchuOrderMapper.selectPayedOrderVoList(yanchuOrder.getSessionId(),PerformConstants.TICKET_TYPE_WECHAT);
        Integer count = 0;
        if (CollectionUtils.isEmpty(list)) {
            return count;
        }
        YanchuRefundOrder refundOrder = null;
        List<YanchuOrderDetail> details = null;
        for (OrderVo order : list) {
            refundOrder = new YanchuRefundOrder();
            refundOrder.setOrderId(order.getId());
            refundOrder.setRefundType(PerformConstants.REFUND_TYPE);
            refundOrder.setRefundFlag(PerformConstants.REFUND_FLAG_TOTAL);
            refundOrder.setRefundReason(reason);
            refundOrder.setCreator(userId);

            details = yanchuOrderDetailMapper.selectPayedOrderDetailList(order.getId());
            List<Long> detailIds = details.stream().map(YanchuOrderDetail::getId).collect(toList());
            String ids = Joiner.on(",").join(detailIds);
            BigDecimal totalRefund = details.stream().map(YanchuOrderDetail::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
            refundOrder.setRefundTotalMoney(totalRefund);
            refundOrder.setDetailIds(ids);
            try {
                count += insertYanchuRefundOrder(refundOrder,session);
                notifyUserCancel(order);//发送消息提醒
            }catch (Exception e){
                logger.error("cancelOrderList error",e);
            }
            logger.info("cancelOrderList count:{}",count);
        }
        return count;
    }

    /**
     * 退款单补偿查询
     * @param refundNo
     * @return
     * @throws WxPayException
     */
    @Override
    public YanchuRefundOrder selectRefundPayByRefundNo(String refundNo) throws WxPayException {
        logger.debug("selectRefundPayByRefundNo orderNo:{}", refundNo);
        YanchuRefundOrder order = yanchuRefundOrderMapper.selectRefundOrderByRefundNo(refundNo);
        if (null == order) {
            return null;
        }
        int count = 0;
        if (order.getRefundStatus() != PerformConstants.REFUND_PROC) {//处理中
            WxPayRefundQueryResult result = wxPayService.refundQuery(null,order.getOrderNo(),refundNo,null);
            if (WxPayConstants.WxpayTradeStatus.SUCCESS.equals(result.getResultCode())) {//已退款补偿
                if(order.getRefundFlag() == 1){
                    count = cancelOrderNotify(order.getOrderNo(),refundNo,BaseWxPayRequest.fen2Yuan(BigDecimal.valueOf(result.getTotalFee())));
                }else if(order.getRefundFlag()==2){
                    count = cancelTicketOrderNotify(refundNo,BaseWxPayRequest.fen2Yuan(BigDecimal.valueOf(result.getTotalFee())));
                }
                logger.debug("selectRefundPayByRefundNo count:{}", count);
            }
        }
        return yanchuRefundOrderMapper.selectRefundOrderByRefundNo(refundNo);
    }

    @Override
    public CancelTicketStatVo cancelTicketStat(Long sessionId) {
        return yanchuRefundOrderMapper.cancelTicketStat(sessionId);
    }


    /**
     * 小程序场次取消消息提醒
     */
    @Override
    public void notifyUserCancel(OrderVo vo) {
        logger.info("notifyUserCancel id:{}",vo.getId());
        if(StringUtils.isEmpty(vo.getOpenId())){
            throw new ServiceException("openId不能为空");
        }
        if (!wxMaService.switchover("wx839ab821b194d9d0")) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置，请核实！", "wx839ab821b194d9d0"));
        }
        try {
            String performName = vo.getPerformVo().getPerformName();
            if(performName.length() > 20){
                performName = performName.substring(0,17)+"...";
            }
            String cancelTips = vo.getSessionVo().getCancelTips();
            if(cancelTips.length() > 20){
                cancelTips = cancelTips.substring(0,17)+"...";
            }
            wxMaService.getMsgService().sendSubscribeMsg(WxMaSubscribeMessage.builder()
                    .templateId(templateIdCancel)
                    .data(com.google.common.collect.Lists.newArrayList(
                            new WxMaSubscribeMessage.MsgData("thing9", performName),
                            new WxMaSubscribeMessage.MsgData("thing7", vo.getSessionVo().getHallName()),
                            new WxMaSubscribeMessage.MsgData("time3", DateUtils.dateTimeHour(vo.getSessionVo().getPlayTime())),
                            new WxMaSubscribeMessage.MsgData("thing4", cancelTips),
                            new WxMaSubscribeMessage.MsgData("thing10", "订单退费中，请稍后查看订单状态和到账情况")))
                            .page("/pages/ticketFolderDetail/index?id="+vo.getId())
                            .miniprogramState(miniprogramState)
                    .toUser(vo.getOpenId())
                    .build());
        }catch (Exception e){
            logger.error("notifyUserCancel error",e);
        }
    }
    @Autowired
    public YanchuRefundOrderServiceImpl(WxMaService wxMaService) {
        this.wxMaService = wxMaService;
    }


    @Override
    public void exportRefundOrderList(HttpServletResponse response, YanchuRefundOrder yanchuRefundOrder) {
        List<ExportOrderRefundListVO> list = new ArrayList<>();
        orderRefundListDataProcessing(selectYanchuRefundOrderList(yanchuRefundOrder),list);
        executeExportOrder(list,response,yanchuRefundOrder.getExportMsg());
    }

    @Override
    public Integer getExportCount(YanchuRefundOrder yanchuRefundOrder) {
        return selectYanchuRefundOrderList(yanchuRefundOrder).size();
    }

    public void orderRefundListDataProcessing(List<RefundOrderVo> refundOrderVoList, List<ExportOrderRefundListVO> exportOrderRefundListVOList){
        //查出所有订单id下的子订单然后塞入
        List<Long> collect = refundOrderVoList.stream().map(RefundOrderVo::getId).collect(Collectors.toList());
        //查询所有退款详情并且塞入

        List<ExcelOrderDetailVo> excelOrderDetailVO = yanchuOrderDetailMapper.getExcelRefundOrderDetailVO(collect).stream().filter(p->p.getStatus()==6).collect(toList());

        Map<Long, List<ExcelOrderDetailVo>> collect1 = excelOrderDetailVO.stream().sorted(Comparator.comparing(ExcelOrderDetailVo::getCreateTime)).collect(Collectors.groupingBy(ExcelOrderDetailVo::getOrderId));

        refundOrderVoList.forEach(yanchuOrderVO -> {
            collect1.forEach((k,v)->{
                if(k.equals(yanchuOrderVO.getId())){
                    yanchuOrderVO.setExcelOrderDetailVoList(v);
                }
            });

            if(null != yanchuOrderVO.getExcelOrderDetailVoList() && yanchuOrderVO.getExcelOrderDetailVoList().size() > 0){
                yanchuOrderVO.getExcelOrderDetailVoList().forEach(details -> {
                    ExportOrderRefundListVO exportOrderListVO = new ExportOrderRefundListVO();
                    exportOrderListVO.setRefundNum(yanchuOrderVO.getRefundNo());
                    exportOrderListVO.setOrderNum(yanchuOrderVO.getOrderNo());
                    exportOrderListVO.setSxjy(yanchuOrderVO.getHallName());
                    exportOrderListVO.setShouName(yanchuOrderVO.getPerformName());
                    exportOrderListVO.setPerformanceCount(yanchuOrderVO.getSessionName());
                    exportOrderListVO.setTicketCount(yanchuOrderVO.getExcelOrderDetailVoList().stream().filter(p -> p.getStatus() == 6).count() + "");
                    exportOrderListVO.setPrice(yanchuOrderVO.getRefundTotalMoney().toString());
                    exportOrderListVO.setReason(yanchuOrderVO.getRefundReason());
                    exportOrderListVO.setOperName(yanchuOrderVO.getApproverName());
                    exportOrderListVO.setRefundTime(DateUtils.parseDateToStr("YYYY/MM/dd HH:mm:ss", yanchuOrderVO.getCreateTime()));
                    exportOrderListVO.setTicketStatus(TicketStatusEnum.indexOf(yanchuOrderVO.getRefundStatus()).getValue());
                    exportOrderListVO.setBuyPeople(yanchuOrderVO.getUserName());
                    exportOrderListVO.setBuyPhoneNumber(yanchuOrderVO.getUserContact());
                    exportOrderListVO.setContactName(yanchuOrderVO.getContactName());
                    exportOrderListVO.setContactPhoneNumber(yanchuOrderVO.getContactPhone());
                    exportOrderListVO.setTicketStalls(details.getPriceName());
                    exportOrderListVO.setPricing(details.getMoney().toString());
                    exportOrderListVO.setSeat(details.getAreaName() + details.getShowNumber());
                    exportOrderListVO.setTicketPeopleName(details.getUserinfoUserName());
                    exportOrderListVO.setIdType(details.getUserinfoIdType() == null ? IdTypeEnum.indexOf(0).getValue() : IdTypeEnum.indexOf(details.getUserinfoIdType()).getValue());
                    exportOrderListVO.setIdNumber(details.getUserinfoIdNumber());
                    exportOrderListVO.setTicketStatus(TicketStatusEnum.indexOf(details.getStatus()).getValue());
                    exportOrderListVO.setYouhuiRelief("0.00");
                    exportOrderListVO.setYouhuihouPrice(details.getMoney().toString());
                    exportOrderListVO.setTicketActualPay(details.getMoney().toString());
                    exportOrderListVO.setRefundStatus(OrderRefundStatusEnum.indexOf(yanchuOrderVO.getRefundStatus()).getValue());
                    exportOrderRefundListVOList.add(exportOrderListVO);
                });
            }
        });
    }

    public void executeExportOrder(List<ExportOrderRefundListVO> list,HttpServletResponse response,String exportMsg){
        // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
        String templateFileName = tempUrl + "orderRefundListExportTemp.xlsx";
        String fileName = "orderList"+System.currentTimeMillis()+".xlsx";
//        String fileName = "C:/Users/lovepig/Desktop/" + "导出订单列表" + System.currentTimeMillis() + ".xlsx";
        try {
            //替换成自己的数据
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            ExcelWriterBuilder write = EasyExcel.write(response.getOutputStream());
            for(int i =0;i<15;i++){
                write.registerWriteHandler(new CustomMergeStrategy(list.stream().map(ExportOrderRefundListVO::getRefundNum).collect(Collectors.toList()), i));
            }
            //只有一个直接写 >1的合并 分开写入
            try (ExcelWriter excelWriter = write.withTemplate(templateFileName).build()){
                WriteSheet writeSheet = EasyExcel.writerSheet().build();
                // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认 是false，会直接使用下一行，如果没有则创建。
                // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
                // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
                // 如果数据量大 list不是最后一行 参照下一个
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                excelWriter.fill(list, fillConfig, writeSheet);
                Map<String, Object> map = MapUtils.newHashMap();
                String[] split = exportMsg.split("/n");
                StringBuilder builder = new StringBuilder();
                for (String s : split) {
                    builder.append(s).append("\n");
                }
                map.put("title", builder.toString());
                excelWriter.fill(map, writeSheet);
            }
        } catch (Exception e) {
            logger.error("executeExportOrder",e);
        }
    }
}
